#pragma once
#include<iostream>
#include<string>
#include<functional>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<cstring>
#include<pthread.h>
#include<memory>
#include"InetAddr.hpp"
#include"Log.hpp"

namespace socket_ns
{
    class Socket;
    const static int gbacklog = 8;
    using socket_sprt = std::shared_ptr<Socket>;//共享指针
    enum 
    {
        SOCKET_ERROR = 1,
        BIND_ERROR,
        LISTEN_ERROR,
        USAGE_ERROR
    };

    class Socket
    {
        public:
        virtual void CreateSocketOrDie() = 0;//设定套接字模型
        virtual void BindSocketOrDie(InetAddr &addr) = 0;
        virtual void ListenSocketOrDie() = 0;//等待和接收传入连接请求的套接字，用于服务器端，在特定端口上监听客户端的连接请求
    
        virtual int Accepter(InetAddr *addr) = 0;//接收网络连接
        virtual bool Connetcor(InetAddr &addr) = 0;//处理客户端的通信和接收请求发送响应
        virtual void SetSocketAddrReuse()=0;//设置套接字地址重用
        virtual int SockFD() = 0;//套接字文件描述符
        virtual int Recv(std::string *out) = 0; //接收
        virtual int Send(const std::string &in) = 0;//发送
        virtual void Close() = 0;//关闭文件

        public:
        void BuildListenSocket(InetAddr &addr)//建立监听套接字
        {
            CreateSocketOrDie();
            SetSocketAddrReuse();
            BindSocketOrDie(addr);
            ListenSocketOrDie();
        }
        bool BuildClientSokcet(InetAddr &addr)
        {
            CreateSocketOrDie();
            return Connetcor(addr);
        }

    };
    class TcpSocket :public Socket
    {
        public:
        TcpSocket(int fd = -1)
        :_sockfd(fd)
        {}
        void CreateSocketOrDie() override
        {
            //黄健流式套接字
            _sockfd = ::socket(AF_INET,SOCK_STREAM,0);
            if(_sockfd<0)
            {
                LOG(FATAL,"socket error");
                exit(SOCKET_ERROR);
            }
            LOG(DEBUG,"socket create success sockfd is:%d\n",_sockfd);
        }
        void BindSocketOrDie(InetAddr &addr) override
        {
            struct sockaddr_in local;
            memset(&local,0,sizeof(local));
            local.sin_family = AF_INET;
            local.sin_port = (addr.Port());
            local.sin_addr.s_addr = inet_addr(addr.Ip().c_str());
            int n = ::bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
            if(n<0)
            {
                LOG(FATAL,"bind error\n");
                exit(BIND_ERROR);
            }
            LOG(DEBUG,"bind success sockfd :%d",_sockfd);
        }
        void ListenSocketOrDie() override
        {
            int n = ::listen(_sockfd,gbacklog);
            if(n<0)
            {
                LOG(FATAL,"listen error\n");
                exit(LISTEN_ERROR);
            }
            LOG(DEBUG,"listen success socket is :%d",_sockfd);
        }
        int Accepter(InetAddr *addr) override
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = ::accept(_sockfd,(struct sockaddr*)&peer,&len);//用于在服务器端处理客户端的连接请求
            //当服务器端的套接字处于监听状态时，这个函数允许服务器接收进入的连接尝试，这个函数时阻塞的，没有客户端连接，它会一直等带，直到客户端连接它位置
            if(sockfd<0)
            {
                LOG(WARNING,"accept error\n");
                return -1;
            }
            *addr = peer;
            return sockfd;
        }
        virtual bool Connetcor(InetAddr &addr)
        {
            struct sockaddr_in server;
            memset(&server,0,sizeof(server));
            server.sin_family = AF_INET;
            server.sin_port= (addr.Port());
            server.sin_addr.s_addr = inet_addr(addr.Ip().c_str());
            int n = connect(_sockfd,(struct sockaddr*)&server,sizeof(server));//客户端发起对服务器的连接请求，//服务器端收到连接请求，发生网络异常导致连接请求终止
            //接收连接并不意味着服务器端已经条用的accept服务器端仅仅是将连接的请求信息记录到等待队列中，因此，connect
            //返回后并不会立即进行数据交换，而是到等待服务器端调用accept之后才能开始数据交换通过read.write等函数
            if(n<0)
            {
                std::cerr<<"connect error"<<std::endl;
                return false;
            }
            return true;
        }
        void SetSocketAddrReuse() override
        {
            int opt =1;
            ::setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR|SO_REUSEADDR,&opt,sizeof(opt));//启用端口复用，允许多个套接字绑定到同一个端口
            //使用SO_REUSEADDR选项可以实现端口复用，这个选项允许套接字和一个已经在使用中的地址绑定
            //允许在端口上启动监听服务，即使执勤的连接仍在使用端口，
            //允许在同一个端口上启动统一服务器的多个实例，只要每个实列绑定不同的本地IP地址，
            //允许单个进程绑定用以端口到多个套接字上，只需要每个绑定指定不同的本地ip地址，
            //对于支持多播的系统，允许完全重复的绑定，通常只对udo套接字适用，
        }
        int Recv(std::string *out) override
        {
            char inbuffer[4096];
            ssize_t n = ::recv(_sockfd,inbuffer,sizeof(inbuffer)-1,0);//函数的作用是从套接字的接收缓冲区中复制数据到指定的缓冲区中。
            //如果套接字的接收缓冲区没有数据，或者其它协议正在接收数据，recv会一直等待，直到协议把数据接收完毕，
            //当协议把数据接收完毕后，recv就会把数据冲套接字的接收缓冲区复制到用户提供的缓冲区中，
            if(n>0)
            {
                inbuffer[n] = 0;
                *out = inbuffer;
            }
            return n;
        }
        int Send(const std::string &in) override
        {
            int n = ::send(_sockfd,in.c_str(),in.size(),0);//将数据发送到已经建立连接的另一端
            //最后一位参数通常设为0，表示阻塞发送，如果发送不成功会一直等待直到数据发送成功/被信号中断，
            return n;
        }
        int SockFD() override
        {
            return _sockfd;
        }
        void Close() override
        {
            if(_sockfd>-1)
            {
                ::close(_sockfd);
            }
        }
        private:
        int _sockfd;//套接字文件描述符
    } ;

}